import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:language_code/language_code.dart';

import '../../js_config.dart';
import '../js_box/js_init.dart';

class JsHttp {
  static Function()? onToLogin;

  static Future<List<dynamic>> event(List<Map<String, dynamic>> body) async {
    Uri uri = JsApp.jsIsDebugScheme ? Uri.http(JsApp.jsHttpEventScheme, JsApp.jsHttpEvent) : Uri.https(JsApp.jsHttpEventScheme, JsApp.jsHttpEvent);

    Map<String, String> headers = await _headers();

    try {
      headers['Content-Type'] = 'application/json';
      final request = http.Request('POST', uri);
      request.headers.addAll(headers);
      request.body = json.encode(body);
      http.Response response = await http.Response.fromStream(await request.send());
      return await _resolverBodys(uri, headers, body, response);
    } catch (e) {
      e.printInfo();
      return [JsApp.jsCodeFailed, false, e.toString()];
    }
  }

  static Future<List<dynamic>> post(String url, bool isJson, [Map<String, dynamic>? body, host = JsApp.jsHttpScheme]) async {
    Uri uri;
    if (url.startsWith("http")) {
      uri = Uri.parse(url);
    } else {
      uri = JsApp.jsIsDebugScheme ? Uri.http(host, url) : Uri.https(host, url);
    }

    debugPrint("[Request Start] ---- Post $uri");

    Map<String, String> headers = await _headers();

    try {
      http.Response response;
      if (JsApp.jsGpTranslate.contains(uri.authority)) {
        Map<String, String> newBody = {};
        body?.forEach((key, value) {
          newBody[key] = value;
        });
        response = await http.post(uri, body: newBody);
      } else {
        if (isJson) {
          headers['Content-Type'] = 'application/json';
          final request = http.Request('POST', uri);
          request.headers.addAll(headers);
          request.body = json.encode(body);
          response = await http.Response.fromStream(await request.send());
        } else {
          Map<String, String> newBody = {};
          body?.forEach((key, value) {
            newBody[key] = value;
          });
          response = await http.post(uri, headers: headers, body: newBody);
        }
      }
      return await _resolverBody(uri, headers, body, response);
    } catch (e) {
      e.printInfo();
      return [JsApp.jsCodeFailed, false, e.toString()];
    }
  }

  static Future<List<dynamic>> get(String url, [Map<String, dynamic>? body, host = JsApp.jsHttpScheme]) async {
    Uri uri;
    if (url.startsWith("http")) {
      uri = Uri.parse(url);
      if (body != null && body.isNotEmpty) {
        body.forEach((key, value) {
          uri.queryParameters[key] = value.toString();
        });
      }
    } else {
      uri = JsApp.jsIsDebugScheme ? Uri.http(host, url, body) : Uri.https(host, url, body);
    }

    debugPrint("[Request Start] ---- Get $uri");

    Map<String, String> headers = await _headers();
    try {
      http.Response response;

      if (JsApp.jsGpTranslate.contains(uri.authority)) {
        response = await http.get(uri);
      } else {
        response = await http.get(uri, headers: headers);
      }

      return await _resolverBody(uri, headers, body, response);
    } catch (e) {
      e.printInfo();
      return [JsApp.jsCodeFailed, false, e.toString()];
    }
  }

  static _headers() async {
    final deviceId = (await JsObjectBox.setting).deviceId;
    final deviceModel = await JsApp.getDeviceModel();
    final pkg = await JsApp.getPackageName();
    final ver = await JsApp.getVersionName();
    final pVer = await JsApp.getBuildNumber();
    final lang = LanguageCode.code.code;
    final token = (await JsObjectBox.setting).userToken;

    Map<String, String> headers = {};

    headers['device-id'] = deviceId;
    headers["platform"] = JsApp.getPlatform();
    headers['model'] = deviceModel;
    headers['pkg'] = pkg;
    headers['ver'] = ver;
    headers['p_ver'] = pVer;
    headers['kst'] = '1';
    headers['sys_lan'] = lang;
    headers['lang'] = lang;
    if (token.isNotEmpty) {
      headers['Authorization'] = 'Bearer$token';
    }
    return headers;
  }

  static Future<List<dynamic>> _resolverBody(
    Uri uri,
    Map<String, String> headers,
    Map<String, dynamic>? body,
    http.Response response,
  ) async {
    final statusCode = response.statusCode;
    String msg;

    if (200 == statusCode) {
      Map<String, dynamic> map = jsonDecode(response.body);
      String code;
      dynamic data;

      if (JsApp.jsGpTranslate.contains(uri.authority)) {
        if (map.containsKey("error")) {
          Map<String, dynamic> errorMap = map["error"];
          code = errorMap["code"].toString();
          msg = errorMap["message"].toString();
          if (JsApp.jsCodeSuccess == code) code = JsApp.jsCodeFailed;
        } else if (map.containsKey("data")) {
          code = JsApp.jsCodeSuccess;
          msg = "Success";
          data = map["data"];
        } else {
          code = JsApp.jsCodeFailed;
          msg = map.toString();
          data = "";
        }
      } else {
        code = map["code"].toString();
        msg = map["msg"].toString();
        data = map["data"];
      }

      if ("0" == code) {
        debugPrint("""[Request Success] --------------
url: ${uri.toString()}
headers: $headers
params: $body
code: $code, key: ${map["key"]}, msg: $msg
data: $data""");

        return [code, true, data];
      } else {
        debugPrint("""[Request Failed] --------------
url: ${uri.toString()}
headers: $headers
params: $body
code: $code
msg: $msg""");

        switch (code) {
          case JsApp.jsCodeInvalidToken:
          case JsApp.jsCodeNoToken:
          case JsApp.jsCodeTokenError:
          case JsApp.jsCodeTokenHasExpired:
          case JsApp.jsCodeLoginOnOtherDevices:
            onToLogin?.call();
            break;
          default:
        }

        return [code, false, msg];
      }
    } else {
      msg = response.reasonPhrase ?? "";

      debugPrint("""[Request Failed] --------------
url: ${uri.toString()}
headers: $headers
params: $body
statusCode: $statusCode
msg: $msg""");

      return [statusCode.toString(), false, msg];
    }
  }

  static Future<List<dynamic>> _resolverBodys(
    Uri uri,
    Map<String, String> headers,
    List<Map<String, dynamic>>? body,
    http.Response response,
  ) async {
    final statusCode = response.statusCode;
    String msg;

    if (200 == statusCode) {
      Map<String, dynamic> map = jsonDecode(response.body);
      String code;
      dynamic data;

      if (JsApp.jsGpTranslate.contains(uri.authority)) {
        if (map.containsKey("error")) {
          Map<String, dynamic> errorMap = map["error"];
          code = errorMap["code"].toString();
          msg = errorMap["message"].toString();
          if (JsApp.jsCodeSuccess == code) code = JsApp.jsCodeFailed;
        } else if (map.containsKey("data")) {
          code = JsApp.jsCodeSuccess;
          msg = "Success";
          data = map["data"];
        } else {
          code = JsApp.jsCodeFailed;
          msg = map.toString();
          data = "";
        }
      } else {
        code = map["code"].toString();
        msg = map["msg"].toString();
        data = map["data"];
      }

      if ("0" == code) {
        debugPrint("""[Request Success] --------------
url: ${uri.toString()}
headers: $headers
params: $body
code: $code, key: ${map["key"]}, msg: $msg
data: $data""");

        return [code, true, data];
      } else {
        debugPrint("""[Request Failed] --------------
url: ${uri.toString()}
headers: $headers
params: $body
code: $code
msg: $msg""");

        switch (code) {
          case JsApp.jsCodeInvalidToken:
          case JsApp.jsCodeNoToken:
          case JsApp.jsCodeTokenError:
          case JsApp.jsCodeTokenHasExpired:
          case JsApp.jsCodeLoginOnOtherDevices:
            onToLogin?.call();
            break;
          default:
        }

        return [code, false, msg];
      }
    } else {
      msg = response.reasonPhrase ?? "";

      debugPrint("""[Request Failed] --------------
url: ${uri.toString()}
headers: $headers
params: $body
statusCode: $statusCode
msg: $msg""");

      return [statusCode.toString(), false, msg];
    }
  }
}
